home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / tuu103_3.zoo / hh / rnews.c < prev   
Encoding:
C/C++ Source or Header  |  1992-10-06  |  9.1 KB  |  372 lines

  1. /* rnews.c - simple rf?news/r[cf]smtp for MiNT + taylor uuxqt.
  2.  * uses pipes instead of tempfiles where possible and tries to handle
  3.  * error conditions gracefully.
  4.  *
  5.  * send bugs + comments to: Juergen Lock <nox@jelal.north.de>
  6.  *
  7.  */
  8.  
  9. #define SYS_RNEWS "/usr/lib/nn/bin/rnews.tos", "rnews", "-z", "\\dev\\stdin"
  10. #define SYS_RSMTP "/usr/lib/nn/bin/rsmtp.ttp", "rsmtp"
  11. #define DEBUG_DIR "/usr/spool/uucp/.Preserve"
  12.  
  13. /* command (execl args) to pipe uncompressed incoming news into */
  14. #ifndef SYS_RNEWS
  15. #define SYS_RNEWS "/usr/bin/rnews.ttp", "rnews", "-z", "\\dev\\stdin"
  16. #endif
  17.  
  18. /* same for incoming smtp batches */
  19. #ifndef SYS_RSMTP
  20. #define SYS_RSMTP "/usr/bin/rsmtp.ttp", "rsmtp"
  21. #endif
  22.  
  23. /* make this 0 if your rnews doesn't hate pipes... */
  24. #define RNEWS_FILE 1
  25.  
  26. /* directory to save input of failed requests to.  define this if you
  27.    want to examine the files yourself because uuxqt discards failed jobs
  28.    that don't want to be retried later (EX_TEMPFAIL). */
  29. /* #define DEBUG_DIR "/usr/spool/uucp/.Preserve" /* */
  30.  
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <stdlib.h>
  34. #include <ioctl.h>
  35. #include <process.h>
  36. #include <unistd.h>
  37. #include <errno.h>
  38.  
  39. /* make sure we have EX_TEMPFAIL */
  40. #ifndef EX_TEMPFAIL
  41. #define EX_TEMPFAIL 75
  42. #endif
  43.  
  44. /* default argv[0] value to use when we're called with argv[0] == "".
  45.    taylor uuxqt never does that (like any program linked with the MiNT
  46.    libraries) but it costs us nothing and maybe its useful some day. */
  47. #ifdef RNEWS
  48. char *argv0 = "rnews";
  49. #endif
  50. #ifdef RFNEWS
  51. char *argv0 = "rfnews";
  52. #endif
  53. #ifdef RSMTP
  54. char *argv0 = "rsmtp";
  55. #endif
  56. #ifdef RCSMTP
  57. char *argv0 = "rcsmtp";
  58. #endif
  59. #ifdef RFSMTP
  60. char *argv0 = "rfsmtp";
  61. #endif
  62.  
  63. #ifdef DEBUG_DIR
  64. int savestdin;
  65. #endif
  66.  
  67. /* pid of the exec'd compress or freeze, if any */
  68. int cpid;
  69. /* read end of the pipe connected to its stderr */
  70. int cerrfd;
  71. /* exit code */
  72. int cret;
  73. #if RNEWS_FILE
  74. char *tmpf = (char *) NULL;
  75. #endif
  76.  
  77. /* pid of rnews/rsmtp */
  78. int rpid;
  79. /* and its exit code */
  80. int ret;
  81.  
  82. /* wait for a child.  always check the pid because we usually don't
  83.    know wich one dies first. */
  84. void cwait ()
  85. {
  86.     int status, pid;
  87.  
  88.     pid = wait (&status);
  89.     if (pid == cpid) {
  90.         cret = status >> 8;
  91.         cpid = 0;
  92.     } else if (pid == rpid) {
  93.         ret = status >> 8;
  94.         rpid = 0;
  95.     }
  96. }
  97.  
  98. /* print error message, cleanup and exit.  if DEBUG_DIR is defined and
  99.    the exit code isn't EX_TEMPFAIL (meaning uuxqt can retry the job
  100.    later) then copy our input. */
  101. void fail (ex, msg)
  102. int ex;
  103. char *msg;
  104. {
  105.     if (msg)
  106.         fprintf (stderr, "%s: %s\n", argv0, msg);
  107.     if (cpid > 0) {
  108.         /* close the pipes */
  109.         close (0);
  110.         close (cerrfd);
  111.         cwait ();
  112.     }
  113. #ifdef DEBUG_DIR
  114.     if (ex != EX_TEMPFAIL) {
  115.         char copyf[sizeof DEBUG_DIR + 20], *buf = malloc (0x4000);
  116.         int fd, count;
  117.  
  118.         /* just copy it to DEBUG_DIR/rnews.123 etc.  if the copy
  119.            fails too return EX_TEMPFAIL because thats the only
  120.            thing we can do so that the file doesn't get lost.
  121.  
  122.            FIXME: should probably write a log file or something... */
  123.  
  124.         lseek (savestdin, 0, SEEK_SET);
  125.         sprintf (copyf, "%s/%s.XXX", DEBUG_DIR, argv0);
  126.         if (!buf || !mktemp (copyf) ||
  127.             (fd = creat (copyf, 0600)) < 0)
  128.             exit (EX_TEMPFAIL);
  129.         while ((count = read (savestdin, buf, 0x4000)) > 0) {
  130.             if (write (fd, buf, count) != count) {
  131.                 close (fd);
  132.                 remove (copyf);
  133.                 exit (EX_TEMPFAIL);
  134.             }
  135.         }
  136.         if (close (fd) < 0 || count < 0) {
  137.             remove (copyf);
  138.             exit (EX_TEMPFAIL);
  139.         }
  140.     }
  141. #endif
  142.     exit (ex);
  143. }
  144.  
  145. int main (argc, argv)
  146. int argc;
  147. char **argv;
  148. {
  149.     char *p;
  150.     int news = 0, uncompressed = 1, freeze = 0;
  151.  
  152.     /* what are we? */
  153.     if (*argv && **argv) {
  154.         argv0 = *argv;
  155.         /* extract the basename, without .ttp etc */
  156.         if (*argv0 && argv0[1] == ':')
  157.             argv0 += 2;
  158.         if (p = strrchr (argv0, '/'))
  159.             argv0 = p + 1;
  160.         if (p = strrchr (argv0, '\\'))
  161.             argv0 = p + 1;
  162.         if (p = strchr (argv0, '.'))
  163.             *p = 0;
  164.     }
  165.     if (!strcmp (argv0, "rnews"))
  166.         news = 1;
  167.     else if (!strcmp (argv0, "rfnews") || !strcmp (argv0, "frnews"))
  168.         news = 1, uncompressed = 0, freeze = 1;
  169.     else if (!strcmp (argv0, "rsmtp"))
  170.         ;
  171.     else if (!strcmp (argv0, "rcsmtp") || !strcmp (argv0, "crsmtp"))
  172.         uncompressed = 0;
  173.     else if (!strcmp (argv0, "rfsmtp") || !strcmp (argv0, "frsmtp"))
  174.         uncompressed = 0, freeze = 1;
  175.     else {
  176.         fprintf (stderr, "%s: what am i?? :-)  unknown argv[0] value.\n", argv0);
  177.         /* input gets not saved here! */
  178.         exit (1);
  179.     }
  180.     if (argc != 1) {
  181.         fprintf (stderr, "%s: hmm... this is ment to be called by uuxqt only, and gets no args.\n", argv0);
  182.         exit (1);
  183.     }
  184.  
  185.     /* if we are rnews we have to look at the input to see how to
  186.        uncompress. */
  187.     if (news && uncompressed) {
  188.         char buf[100];
  189.         long start;
  190.  
  191.         if (read (0, buf, sizeof buf) < sizeof buf - 1)
  192.             fail (1, "input too short, can hardly be a news batch...");
  193.  
  194.         buf[sizeof buf - 1] = 0;
  195.         if (*buf != '#' || buf[1] != '!' ||
  196.             !(p = strchr (buf + 2, '\n')) ||
  197.             ((start = p - buf + 1), (*p = 0),
  198.              (p = buf + 2 + strspn (buf + 2, " \t"))) >= buf + start ||
  199.             (!(uncompressed = !strncmp (p, "rnews", sizeof "rnews" -1)) &&
  200.              !(freeze = !strcmp (p, "funbatch")) &&
  201.              strcmp (p, "cunbatch") != 0))
  202.  
  203.             fail (1, "unknown format, don't know how to uncompress.");
  204.  
  205.         /* now seek back to where the fun begins. */
  206.         if (uncompressed)
  207.             /* `#! rnews' is already part of the batch */
  208.             start = 0;
  209.         /* ...else skip the `#! [cf]unbatch'. */
  210.         if (lseek (0, start, SEEK_SET) != start)
  211.             fail (1, "huh!?  can't lseek back on input!");
  212.     }
  213.  
  214.     /* if input is compressed set up a pipe to uncompress. */
  215.     if (!uncompressed) {
  216.         int pipefd[2], perrfd[2];
  217.         char *compress = freeze ? "freeze" : "compress";
  218.  
  219.         if (pipe (pipefd) < 0 || pipe (perrfd) < 0 || (cpid = fork()) < 0)
  220.             fail (EX_TEMPFAIL, "pipe or fork failed. (not enough memory?)");
  221.  
  222.         if (!cpid) {
  223.             /* child */
  224.  
  225.             /* close read ends of the pipes */
  226.             close (pipefd[0]);
  227.             close (perrfd[0]);
  228.             /* connect compress/freeze's stdout and stderr to
  229.                the pipes... */
  230.             if (dup2 (pipefd[1], 1) < 0 || dup2 (perrfd[1], 2) < 0)
  231.                 exit (EX_TEMPFAIL);
  232.             close (pipefd[1]);
  233.             close (perrfd[1]);
  234.             /* ...and exec it. */
  235.             execlp (compress, compress, "-d", (char *) NULL);
  236.             /* exec failed, probably also temporary. */
  237.             perror (compress);
  238.             exit (EX_TEMPFAIL);
  239.         }
  240.         /* parent */
  241.  
  242.         /* close write ends */
  243.         close (pipefd[1]);
  244.         close (perrfd[1]);
  245.  
  246.         /* save stderr */
  247.         cerrfd = perrfd[0];
  248. #ifdef DEBUG_DIR
  249.         /* save stdin in case we have to copy it... */
  250.         savestdin = dup (0);
  251. #endif
  252.         /* connect stdin to the uncompressed output */
  253.         if (dup2 (pipefd[0], 0) < 0) {
  254.             close (pipefd[0]);
  255.             fail (EX_TEMPFAIL, strerror (errno));
  256.         }
  257.         close (pipefd[0]);
  258.     }
  259.  
  260. #if RNEWS_FILE
  261.     /* DI rnews wants a file as input so we make one. (poor /tmp disk..) */
  262.  
  263.     if (news && !uncompressed) {
  264.         int count, fd;
  265.         char *buf = malloc (0x4000);
  266.  
  267.         if (!buf || !(tmpf = tmpnam((char *) NULL)) ||
  268.             (fd = creat (tmpf, 0600)) < 0)
  269.             fail (EX_TEMPFAIL, strerror (errno));
  270.         while ((count = read (0, buf, 0x4000)) > 0) {
  271.             if (write (fd, buf, count) != count) {
  272.                 int err = errno;
  273.  
  274.                 close (fd);
  275.                 unlink (tmpf);
  276.                 free (tmpf);
  277.                 free (buf);
  278.                 fail (EX_TEMPFAIL, strerror (err));
  279.             }
  280.         }
  281.         free (buf);
  282.         if (count < 0 || lseek (fd, 0l, SEEK_SET) ||
  283.             dup2 (fd, 0) < 0) {
  284.             int err = errno;
  285.  
  286.             close (fd);
  287.             unlink (tmpf);
  288.             free (tmpf);
  289.             free (buf);
  290.             fail (EX_TEMPFAIL, strerror (err));
  291.         }
  292.         close (fd);
  293.         free (buf);
  294.     }
  295. #endif /* RNEWS_FILE */
  296.  
  297.     /* stdin now is the uncompressed batch either as file or pipe */
  298.     if (news)
  299.         rpid = spawnl (P_NOWAIT, SYS_RNEWS, (char *) NULL);
  300.     else
  301.         rpid = spawnl (P_NOWAIT, SYS_RSMTP, (char *) NULL);
  302.  
  303.     if (rpid == -1)
  304.         /* if the spawn itself failed assume it can be retried. */
  305.         fail (EX_TEMPFAIL, strerror (errno));
  306.  
  307.     if (uncompressed)
  308.         /* wait for the child */
  309.         cwait ();
  310.     else {
  311.         char errmsg [0x200];
  312.         long clen;
  313.  
  314.         /* compress and freeze don't return special error codes
  315.            for `not enough memory', so we read its stderr. */
  316.         if (ioctl (cerrfd, FIONREAD, &clen) < 0) {
  317.             int err = errno;
  318.  
  319.             cwait ();
  320.             cwait ();
  321. #if RNEWS_FILE
  322.             if (tmpf) {
  323.                 /* kill the tempfile */
  324.                 close (0);
  325.                 unlink (tmpf);
  326.                 free (tmpf);
  327.             }
  328. #endif
  329.             fail (EX_TEMPFAIL, strerror (err));
  330.         }
  331.         if (clen > 0)
  332.             clen = read (cerrfd, errmsg, sizeof errmsg - 1);
  333.         else
  334.             clen = 0;
  335.         errmsg[clen] = 0;
  336.  
  337.         /* close the pipes */
  338.         close (0);
  339.         close (cerrfd);
  340.  
  341.         /* wait for both childs */
  342.         cwait();
  343.         cwait();
  344.  
  345. #if RNEWS_FILE
  346.         if (tmpf) {
  347.             /* kill the tempfile */
  348.             unlink (tmpf);
  349.             free (tmpf);
  350.         }
  351. #endif
  352.         if (cret) {
  353.             /* kill trailing \n in errmsg because fail will
  354.                append one itself. */
  355.             if (clen && errmsg[clen - 1] == '\n')
  356.                 errmsg[clen - 1] = 0;
  357.             if (strstr (errmsg, "nsufficient memory") ||
  358.                 strstr (errmsg, "eap overflow") ||
  359.                 strstr (errmsg, "ut of memory"))
  360.                 cret = EX_TEMPFAIL;
  361.             if (!*errmsg)
  362.                 fail (cret, (char *) NULL);
  363.             fail (cret, errmsg);
  364.         }
  365.     }
  366.  
  367.     if (!ret || (ret = EX_TEMPFAIL))
  368.         exit(ret);
  369.  
  370.     fail (ret, (char *) NULL);
  371. }
  372.